#include <mex.h>
#include <vector>
#include <string>
#include "daqAdapter.h"
using namespace std;

bool initialized = false;						// initialization flag
static DaqGroupIO *daqIn=0, *daqOut=0;

void mxCellStringToVectorString(const mxArray* in, vector<string>& out)
{
	size_t sz=mxGetN(in);
	mexPrintf("cell array of size %d\n", sz);
	
	out.clear();
	out.reserve(sz);
	
	for(unsigned int i=0;i<sz;i++)
	{
		char buf[256];
		mxGetString(mxGetCell(in, i), buf, sizeof(buf));
		mexPrintf("%d %s\n", i, buf);
		out.push_back(buf);
	}
}

mxArray* daqInit(const mxArray* mxInSpec, const mxArray* mxOutSpec, int rate)
{

	vector<string> inSpec, outSpec;
	mxCellStringToVectorString(mxInSpec, inSpec);
	mxCellStringToVectorString(mxOutSpec, outSpec);

	daqIn=new DaqGroupIO(inSpec, rate, -1, 6);
	daqOut=new DaqGroupIO(outSpec,rate, 2, 8);
	
	const char* fieldNames[] = {"numChanOut","numChanIn"};
	mxArray* info = mxCreateStructMatrix(1,1, 2,fieldNames); 
	mxSetField(info, 0, "numChanOut", mxCreateDoubleScalar(daqOut->numChan));
	mxSetField(info, 0, "numChanIn", mxCreateDoubleScalar(daqIn->numChan));
/* Matlab struct creation
	// create info structure
	const int numfields = 3;
	const char* names[numfields] = {
		"input", "output", "limit"
	};
	F
	mxSetField(info, 0, "input", mxCreateCharMatrixFromStrings(inGroups, inDevice));
	mxSetField(info, 0, "output", mxCreateCharMatrixFromStrings(outGroups, outDevice));

	return info;
	
	// set limits to Matlab
	mxArray* res = mxCreateDoubleMatrix(2, 2, mxREAL);
	ptr = mxGetPr(res);
	ptr[0] = limits[0][0];
	ptr[1] = limits[1][0];
	ptr[2] = limits[0][1];
	ptr[3] = limits[1][1];
	
	*/
	//daq=DaqGroupIO(
	initialized=true;
	return info;
}

int daqRead(mxArray** pout)
{
	*pout = mxCreateDoubleMatrix(1, daqIn->numChan, mxREAL);
	double *ptr = mxGetPr(*pout);
	return daqIn->read(ptr);
}
int daqWrite(const mxArray* pin)
{
	double *ptr = mxGetPr(pin);
	return daqOut->write(ptr);
}



//------------------------------------------------------------------------------------------
// check matlab argument, of #elements n
void mxcheck(const mxArray* mx, const int n)
{
	if( mxGetClassID(mx) != mxDOUBLE_CLASS )
		mexErrMsgTxt("argument must be double");
	if( mxGetNumberOfDimensions(mx) != 2 )
		mexErrMsgTxt("argument must be 2D array");
	const mwSize* sz = mxGetDimensions(mx);
	if( sz[0]*sz[1] != n )
	{
		mexPrintf("sz1= %d,  sz2= %d,  n= %d\n", sz[0], sz[1], n);
		mexErrMsgTxt("argument has the wrong size");
	}
}

//-----------------------------------------------------------------------------------------
// function called at mex exit

void exitFunction(void)
{
	if(initialized)
	{
		initialized = false;
		if(daqIn) delete daqIn;
		if(daqOut) delete daqOut;
		
		mexUnlock();
	}
}


//-----------------------------------------------------------------------------------------
// mex entry point: process and dispatch user commands

void mexFunction(int nout, mxArray* pout[], int nin, const mxArray* pin[])
{
	// register exit function
	if( !initialized )
		mexAtExit(exitFunction);

	// no inputs: print help and status, return
	if( !nin )
	{
		mexPrintf("NIDAQ Matlab interface:\n");
		mexPrintf("  clear: daqAdapterMex(0)\n");
		mexPrintf("   init: info = daqAdapterMex(1, ioRate, {'Dev1/ai0:6'}, {'Dev1/ao0:2'})\n");
		mexPrintf("   read: [data] = daqAdapterMex(2)\n");
		mexPrintf("  write: daqAdapterMex(3, data)\n");
		if( initialized )
			mexPrintf("Status: initialized\n");
		else
			mexPrintf("Status: not initialized\n");

		return;
	}

	// get command
	int comid = -1;
	if( mxGetClassID(pin[0])==mxDOUBLE_CLASS )
		comid = (int)*(double*)mxGetData(pin[0]);

	// dispatch command
	switch( comid )
	{
	case 0:							// clear
		if( !initialized )
			mexPrintf("not initialized\n");
		else
			exitFunction();
		break;

	case 1:							// init
		if( initialized )
			mexErrMsgTxt("already initialized\n");
		else
		{
			if (nin==4)
			{
				pout[0] = daqInit(pin[2], pin[3], (int)mxGetScalar(pin[1]));
				initialized = true;
				mexLock();
			}else{
				mexErrMsgTxt("Wrong # argument. Should be 4");
			}
		}
		break;

	case 2:							// read
		if( !initialized )
			mexErrMsgTxt("must initialize before calling 'read'");

		daqRead(&pout[0]);
		break;

	case 3:							// write
		if( !initialized )
			mexErrMsgTxt("must initialize before calling 'write'");

		daqWrite(pin[1]);
		break;

	default:
		mexErrMsgTxt("invalid command");
		return;
	}
}	